home *** CD-ROM | disk | FTP | other *** search
/ Aminet 24 / Aminet 24 (1998)(GTI - Schatztruhe)[!][Apr 1998].iso / Aminet / comm / mail / Mutt089src.lha / Mutt-0.89i-AMIGA / src / mx.c < prev    next >
C/C++ Source or Header  |  1998-01-28  |  29KB  |  1,384 lines

  1. /*
  2.  * Copyright (C) 1996-8 Michael R. Elkins <me@cs.hmc.edu>
  3.  * 
  4.  *     This program is free software; you can redistribute it and/or modify
  5.  *     it under the terms of the GNU General Public License as published by
  6.  *     the Free Software Foundation; either version 2 of the License, or
  7.  *     (at your option) any later version.
  8.  * 
  9.  *     This program is distributed in the hope that it will be useful,
  10.  *     but WITHOUT ANY WARRANTY; without even the implied warranty of
  11.  *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12.  *     GNU General Public License for more details.
  13.  * 
  14.  *     You should have received a copy of the GNU General Public License
  15.  *     along with this program; if not, write to the Free Software
  16.  *     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  17.  */ 
  18.  
  19. #include "mutt.h"
  20. #include "mx.h"
  21. #include "send.h"
  22. #include "rfc2047.h"
  23. #include "sort.h"
  24. #include "mailbox.h"
  25. #include "state.h"
  26. #include "parse.h"
  27. #include "copy.h"
  28.  
  29. #ifdef _PGPPATH
  30. #include "pgp.h"
  31. #endif
  32.  
  33. #ifdef BUFFY_SIZE
  34. #include "buffy.h"
  35. #endif
  36.  
  37. #include <dirent.h>
  38. #include <fcntl.h>
  39. #include <sys/file.h>
  40. #include <sys/stat.h>
  41. #include <errno.h>
  42. #include <unistd.h>
  43. #include <stdlib.h>
  44. #include <string.h>
  45. #include <ctype.h>
  46. #ifndef BUFFY_SIZE
  47. #include <utime.h>
  48. #endif
  49.  
  50. /* HP-UX and ConvexOS don't have this macro */
  51. #ifndef S_ISLNK
  52. #define S_ISLNK(x) (((x) & S_IFMT) == S_IFLNK ? 1 : 0)
  53. #endif
  54.  
  55. #define mutt_is_spool(s)  (strcmp (Spoolfile, s) == 0)
  56.  
  57. #define MAXLOCKATTEMPT 5
  58.  
  59. #ifdef USE_DOTLOCK
  60. static int dotlock_file (const char *path)
  61. {
  62.   const char *pathptr = path;
  63.   char lockfile[_POSIX_PATH_MAX];
  64.   char nfslockfile[_POSIX_PATH_MAX];
  65.   char realpath[_POSIX_PATH_MAX];
  66.   struct stat sb;
  67.   int count = 0;
  68.   int fd;
  69.  
  70.   /* if the file is a symlink, find the real file to which it refers */
  71.   FOREVER
  72.   {
  73.     dprint(2,(debugfile,"dotlock_file(): locking %s\n", pathptr));
  74.  
  75.     if (lstat (pathptr, &sb) != 0)
  76.     {
  77.       mutt_perror (pathptr);
  78.       return (-1);
  79.     }
  80.  
  81.     if (S_ISLNK (sb.st_mode))
  82.     {
  83.       char linkfile[_POSIX_PATH_MAX];
  84.       char linkpath[_POSIX_PATH_MAX];
  85.  
  86.       if ((count = readlink (pathptr, linkfile, sizeof (linkfile))) == -1)
  87.       {
  88.     mutt_perror (path);
  89.     return (-1);
  90.       }
  91.       linkfile[count] = 0; /* readlink() does not NUL terminate the string! */
  92.       mutt_expand_link (linkpath, pathptr, linkfile);
  93.       strfcpy (realpath, linkpath, sizeof (realpath));
  94.       pathptr = realpath;
  95.     }
  96.     else
  97.       break;
  98.   }
  99.  
  100.   snprintf (nfslockfile, sizeof (nfslockfile), "%s.%s.%d", pathptr, Hostname, (int) getpid ());
  101.   snprintf (lockfile, sizeof (lockfile), "%s.lock", pathptr);
  102.   unlink (nfslockfile);
  103.  
  104.   while ((fd = open (nfslockfile, O_WRONLY | O_EXCL | O_CREAT, 0)) < 0)
  105.     if (errno != EAGAIN)
  106.     {
  107.       mutt_perror ("cannot open NFS lock file!");
  108.       return (-1);
  109.     }
  110.  
  111.   close (fd);
  112.  
  113.   count = 0;
  114.   FOREVER
  115.   {
  116.     link (nfslockfile, lockfile);
  117.     if (stat (nfslockfile, &sb) != 0)
  118.     {
  119.       mutt_perror ("stat");
  120.       return (-1);
  121.     }
  122.  
  123.     if (sb.st_nlink == 2)
  124.       break;
  125.  
  126.     if (++count == MAXLOCKATTEMPT)
  127.     {
  128.       if (mutt_yesorno ("Lock count exceeded, remove lock?", 1) == 1)
  129.       {
  130.     unlink (lockfile);
  131.     count = 0;
  132.     continue;
  133.       }
  134.       else
  135.     return (-1);
  136.     }
  137.  
  138.     mutt_message ("Waiting for lock attempt #%d...", count);
  139.     sleep (1);
  140.   }
  141.  
  142.   unlink (nfslockfile);
  143.  
  144.   return 0;
  145. }
  146.  
  147. static int undotlock_file (const char *path)
  148. {
  149.   const char *pathptr = path;
  150.   char lockfile[_POSIX_PATH_MAX];
  151.   char realpath[_POSIX_PATH_MAX];
  152.   struct stat sb;
  153.   int n;
  154.  
  155.   FOREVER
  156.   {
  157.     dprint (2,(debugfile,"undotlock: unlocking %s\n",path));
  158.  
  159.     if (lstat (pathptr, &sb) != 0)
  160.     {
  161.       mutt_perror (pathptr);
  162.       return (-1);
  163.     }
  164.  
  165.     if (S_ISLNK (sb.st_mode))
  166.     {
  167.       char linkfile[_POSIX_PATH_MAX];
  168.       char linkpath[_POSIX_PATH_MAX];
  169.  
  170.       if ((n = readlink (pathptr, linkfile, sizeof (linkfile))) == -1)
  171.       {
  172.     mutt_perror (pathptr);
  173.     return (-1);
  174.       }
  175.       linkfile[n] = 0; /* readlink() does not NUL terminate the string! */
  176.       mutt_expand_link (linkpath, pathptr, linkfile);
  177.       strfcpy (realpath, linkpath, sizeof (realpath));
  178.       pathptr = realpath;
  179.       continue;
  180.     }
  181.     else
  182.       break;
  183.   }
  184.  
  185.   snprintf (lockfile, sizeof (lockfile), "%s.lock", pathptr);
  186.   unlink (lockfile);
  187.   return 0;
  188. }
  189. #endif /* USE_DOTLOCK */
  190.  
  191. /* Args:
  192.  *    excl        if excl != 0, request an exclusive lock
  193.  *    dot        if dot != 0, try to dotlock the file
  194.  */
  195. int mx_lock_file (const char *path, int fd, int excl, int dot)
  196. {
  197. #if defined (USE_FCNTL) || defined (USE_FLOCK)
  198.   int count;
  199. #endif
  200.   int r = 0;
  201.  
  202. #ifdef USE_FCNTL
  203.   struct flock lck;
  204.  
  205.   memset (&lck, 0, sizeof (struct flock));
  206.   lck.l_type = excl ? F_WRLCK : F_RDLCK;
  207.   lck.l_whence = SEEK_SET;
  208.  
  209.   count = 0;
  210.   while (fcntl (fd, F_SETLK, &lck) == -1)
  211.   {
  212.     dprint(1,(debugfile, "mx_lock_file(): fcntl errno %d.\n", errno));
  213.     if (errno != EAGAIN && errno != EACCES)
  214.     {
  215.       mutt_perror ("fcntl");
  216.       return (-1);
  217.     }
  218.  
  219.     if (++count == MAXLOCKATTEMPT)
  220.     {
  221.       mutt_error ("Timeout exceeded while attempting fcntl lock!");
  222.       return (-1);
  223.     }
  224.  
  225.     mutt_message ("Waiting for fcntl lock... %d", count);
  226.     sleep (1);
  227.   }
  228. #endif /* USE_FCNTL */
  229.  
  230. #ifdef USE_FLOCK
  231.   count = 0;
  232.   while (flock (fd, (excl ? LOCK_EX : LOCK_SH) | LOCK_NB) == -1)
  233.   {
  234.     if (errno != EWOULDBLOCK)
  235.     {
  236.       mutt_perror ("flock");
  237.       r = -1;
  238.       break;
  239.     }
  240.  
  241.     if (++count == MAXLOCKATTEMPT)
  242.     {
  243.       mutt_error ("Timeout exceeded while attempting flock lock!");
  244.       r = -1;
  245.       break;
  246.     }
  247.  
  248.     mutt_message ("Waiting for flock attempt... %d", count);
  249.     sleep (1);
  250.   }
  251. #endif /* USE_FLOCK */
  252.  
  253. #ifdef USE_DOTLOCK
  254.   if (r == 0 && dot)
  255.     r = dotlock_file (path);
  256. #endif /* USE_DOTLOCK */
  257.  
  258.   if (r == -1)
  259.   {
  260.     /* release any other locks obtained in this routine */
  261.  
  262. #ifdef USE_FCNTL
  263.     lck.l_type = F_UNLCK;
  264.     fcntl (fd, F_SETLK, &lck);
  265. #endif /* USE_FCNTL */
  266.  
  267. #ifdef USE_FLOCK
  268.     flock (fd, LOCK_UN);
  269. #endif /* USE_FLOCK */
  270.  
  271.     return (-1);
  272.   }
  273.  
  274.   return 0;
  275. }
  276.  
  277. int mx_unlock_file (const char *path, int fd)
  278. {
  279. #ifdef USE_FCNTL
  280.   struct flock unlockit = { F_UNLCK, 0, 0, 0 };
  281.  
  282.   memset (&unlockit, 0, sizeof (struct flock));
  283.   unlockit.l_type = F_UNLCK;
  284.   unlockit.l_whence = SEEK_SET;
  285.   fcntl (fd, F_SETLK, &unlockit);
  286. #endif
  287.  
  288. #ifdef USE_FLOCK
  289.   flock (fd, LOCK_UN);
  290. #endif
  291.  
  292. #ifdef USE_DOTLOCK
  293.   undotlock_file (path);
  294. #endif
  295.   
  296.   return 0;
  297. }
  298.  
  299. /* open a file and lock it */
  300. FILE *mx_open_file_lock (const char *path, const char *mode)
  301. {
  302.   FILE *f;
  303.  
  304.   if ((f = fopen (path, mode)) != NULL)
  305.   {
  306.     if (mx_lock_file (path, fileno (f), *mode != 'r', 1) != 0)
  307.     {
  308.       fclose (f);
  309.       f = NULL;
  310.     }
  311.   }
  312.  
  313.   return (f);
  314. }
  315.  
  316. /* try to figure out what type of mailbox ``path'' is
  317.  *
  318.  * return values:
  319.  *    M_*    mailbox type
  320.  *    0    not a mailbox
  321.  *    -1    error
  322.  */
  323. int mx_get_magic (const char *path)
  324. {
  325.   struct stat st;
  326.   int magic = 0;
  327.   char tmp[_POSIX_PATH_MAX];
  328.   FILE *f;
  329.  
  330. #ifdef USE_IMAP
  331.   if (*path == '{')
  332.     return M_IMAP;
  333. #endif /* USE_IMAP */
  334.  
  335.   if (stat (path, &st) == -1)
  336.   {
  337.     dprint (1, (debugfile, "mx_get_magic(): unable to stat %s: %s (errno %d).\n",
  338.         path, strerror (errno), errno));
  339.     mutt_perror (path);
  340.     return (-1);
  341.   }
  342.  
  343.   if (S_ISDIR (st.st_mode))
  344.   {
  345.     /* check for maildir-style mailbox */
  346.  
  347.     snprintf (tmp, sizeof (tmp), "%s/cur", path);
  348.     if (stat (tmp, &st) == 0 && S_ISDIR (st.st_mode))
  349.       return (M_MAILDIR);
  350.  
  351.     /* check for mh-style mailbox */
  352.  
  353.     snprintf (tmp, sizeof (tmp), "%s/.mh_sequences", path);
  354.     if (access (tmp, F_OK) == 0)
  355.       return (M_MH);
  356.  
  357.     snprintf (tmp, sizeof (tmp), "%s/.xmhcache", path);
  358.     if (access (tmp, F_OK) == 0)
  359.       return (M_MH);
  360.   }
  361.   else if (st.st_size == 0)
  362.   {
  363.     /* hard to tell what zero-length files are, so assume the default magic */
  364.     if (DefaultMagic == M_MBOX || DefaultMagic == M_MMDF)
  365.       return (DefaultMagic);
  366.     else
  367.       return (M_MBOX);
  368.   }
  369.   else if ((f = fopen (path, "r")) != NULL)
  370.   {
  371. #ifndef BUFFY_SIZE
  372.     struct utimbuf times;
  373. #endif
  374.  
  375.     fgets (tmp, sizeof (tmp), f);
  376.     if (strncmp ("From ", tmp, 5) == 0)
  377.       magic = M_MBOX;
  378.     else if (strcmp (MMDF_SEP, tmp) == 0)
  379.       magic = M_MMDF;
  380.